﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SeasideResearch.LibCurlNet;
using System.Security.Cryptography;

namespace HTTPPLay
{
    class SimpleHCPIO_LibCurl
    {
        /**
         * Method to read content from HCP NetSystem and create a local disk file with ".fromHCP"
         * appended on the end.
         **/
        public void do_read(Easy inEasy, String inputFilePath, String inFileName)
        {
            CURLcode easyRetVal;
            String fileNameToWrite = inFileName + ".fromHCP";

            /*
             * Create the file we will write the content to.
             */
            System.IO.FileStream outputFileStream = System.IO.File.Create(fileNameToWrite);

            /*
             * Setup the HTTP GET request.
             */
            inEasy.SetOpt(CURLoption.CURLOPT_HTTPGET, 1);
            inEasy.SetOpt(CURLoption.CURLOPT_URL, inputFilePath + "/" + inFileName);
            
            // This sets up a the function we defined that will write the content to the local file.
            inEasy.SetOpt(CURLoption.CURLOPT_WRITEFUNCTION, new Easy.WriteFunction(OnWriteData));
            inEasy.SetOpt(CURLoption.CURLOPT_WRITEDATA, outputFileStream);
            
            /*
             * Perform the HTTP GET request.
             */
            easyRetVal = inEasy.Perform();

            // Make sure we succeeded.
            if (CURLcode.CURLE_OK != easyRetVal)
            {
                if (null != outputFileStream) outputFileStream.Dispose();

                throw new SystemException("Error reading path \"" + inputFilePath + "/" + inFileName + "\" from HCP!! Return Code: " + easyRetVal);
            }

            // Check the response code.
            int responseCode = 0;
            inEasy.GetInfo(CURLINFO.CURLINFO_RESPONSE_CODE, ref responseCode);

            if (200 != responseCode) {
                throw new SystemException("Unexpeced HTTP response code received (" + responseCode + ").");
            }

            /* Outta here!! */
            outputFileStream.Close();
        }

        /**
         * Method to write content to HCP System from a local disk file with ".toHCP" 
         * appended on the end.
         **/
        public void do_write(Easy inEasy, String dstFilePath, String inFileName)
        {
            CURLcode easyRetVal;
            String fileNameToRead = inFileName + ".toHCP";

            /*
             * Make sure the file we are going to write exists on the local file system.
             */
            if ( ! System.IO.File.Exists(inFileName + ".toHCP") )
            {
                Console.WriteLine("File \"{0}\" does not exist!", fileNameToRead);
                throw new System.IO.FileNotFoundException("Unable to locate file to write to HCP", fileNameToRead);
            }

            // Open it for read.
            System.IO.FileStream fileStreamToRead = System.IO.File.OpenRead(fileNameToRead);


            /*
             * Setup the HTTP UPLOAD/PUT request.
             */
            inEasy.SetOpt(CURLoption.CURLOPT_UPLOAD, 1);
            inEasy.SetOpt(CURLoption.CURLOPT_URL, dstFilePath + "/" + inFileName);
            
            // Setup the curl call for reading the local file using the function we defined.
            inEasy.SetOpt(CURLoption.CURLOPT_READFUNCTION, new Easy.ReadFunction(OnReadData));
            inEasy.SetOpt(CURLoption.CURLOPT_READDATA, fileStreamToRead);

            // Tell curl the file length.
            inEasy.SetOpt(CURLoption.CURLOPT_INFILESIZE, fileStreamToRead.Length);

            /*
             * Perform the HTTP UPLOAD/PUT request.
             */
            easyRetVal = inEasy.Perform();

            // Make sure we succeeded.
            if (CURLcode.CURLE_OK != easyRetVal)
            {
                if (null != fileStreamToRead) fileStreamToRead.Dispose();

                throw new SystemException("Error writing file \"" + fileNameToRead + "\" to \"" + dstFilePath + "/" + inFileName + "\" onto HCP!! Return Code: " + easyRetVal);
            }

            // Check the response code.
            int responseCode = 0;
            inEasy.GetInfo(CURLINFO.CURLINFO_RESPONSE_CODE, ref responseCode);

            if (201 != responseCode)
            {
                throw new SystemException("Unexpeced HTTP response code received (" + responseCode + ").");
            }

            /* Outta here!! */
            fileStreamToRead.Close();
        }

        /**
         * Method to write content to HCP System from a local disk file with ".toHCP" 
         * appended on the end.
         **/
        public void do_delete(Easy inEasy, String dstFilePath, String inFileName)
        {
            CURLcode easyRetVal;
            /*
             * Setup the HTTP UPLOAD/PUT request.
             */
            inEasy.SetOpt(CURLoption.CURLOPT_CUSTOMREQUEST, "DELETE");
            inEasy.SetOpt(CURLoption.CURLOPT_URL, dstFilePath + "/" + inFileName);

            /*
             * Perform the HTTP UPLOAD/PUT request.
             */
            easyRetVal = inEasy.Perform();

            // Make sure we succeeded.
            if (CURLcode.CURLE_OK != easyRetVal)
            {
                throw new SystemException("Error deleting file \"" + dstFilePath + "/" + inFileName + "\" from HCP!! Return Code: " + easyRetVal);
            }

            // Check the response code.
            int responseCode = 0;
            inEasy.GetInfo(CURLINFO.CURLINFO_RESPONSE_CODE, ref responseCode);

            if (200 != responseCode)
            {
                throw new SystemException("Unexpeced HTTP response code received (" + responseCode + ").");
            }
        }

        public static void Main(String[] args)
        {
            try
            {
                /*
                 * Make sure we have the right number of program parameters.
                 */
                if (args.Length != 2) {
                    Console.Error.WriteLine("\nUsage: {0} <base-file-name> <hcp-folder-URL>\n");
                    throw new SystemException("Incorrect parameter list");
                }

                /*
                 * Setup our CURL environment and create the easy connection.
                 */
                Curl.GlobalInit((int)CURLinitFlag.CURL_GLOBAL_ALL);

                Easy easy = new Easy();

                /*
                 * Bypass any SSL Key checks because we are not using site specific keys.
                 *   Not needed if only doing HTTP.
                 */
                easy.SetOpt(CURLoption.CURLOPT_SSL_VERIFYPEER, 0);
                easy.SetOpt(CURLoption.CURLOPT_SSL_VERIFYHOST, 1);

                /* 
                 * For debugging purposes, setup the connection to call the header function so we can
                 * see easily see what was returned by HCP in the HTTP header.
                 */
                easy.SetOpt(CURLoption.CURLOPT_HEADERFUNCTION, new Easy.HeaderFunction(headerFunction));

                String authentication = "Authorization: HCP " + Base64Encode("MyDataAccess") + ":" + MD5Encrypt("letmein01");

                easy.SetOpt(CURLoption.CURLOPT_HTTPHEADER, authentication);

                /*
                 * Perform the operations!!
                 */
                SimpleHCPIO_LibCurl myObject = new SimpleHCPIO_LibCurl();

                // Write the content to the HCP System.
                myObject.do_write(easy, args[1], args[0]);
             
                // Read it back from HCP system and onto local disk.
                myObject.do_read(easy, args[1], args[0]);

                // Delete the item from HCP.
                myObject.do_delete(easy, args[1], args[0]);

                /*
                 * Whee!  Wasn't that fun!
                 */
                easy.Cleanup();

                Curl.GlobalCleanup();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        static String MD5Encrypt(String inString)
        {
            MD5CryptoServiceProvider hasher = new MD5CryptoServiceProvider();

            byte[] tBytes = Encoding.ASCII.GetBytes(inString);
            byte[] hBytes = hasher.ComputeHash(tBytes);

            StringBuilder sb = new StringBuilder();
            for (int i=0; i<hBytes.Length; i++)
                sb.AppendFormat("{0:x2}", hBytes[i]);

            return sb.ToString();
        }

        static String Base64Encode(String inString)
        {
            byte[] toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(inString);

            return System.Convert.ToBase64String(toEncodeAsBytes);
        }

        public static int headerFunction(byte[] buf, int size, int nmemb, object extraData)
        {
            char[] buff = new char[(size*nmemb)+1];
            byte[] inPtr = buf;

            int outIdx = 0;
            int inIdx = 0;

            for (; inIdx < size * nmemb; inIdx++)
            {
                buff[outIdx++] = (char)(inPtr[inIdx]);
            }

            buff[outIdx] = '\0';

            Console.Write(buff);

            return nmemb * size;
        }

        public static Int32 OnWriteData(Byte[] buf, Int32 size, Int32 nmemb,
            Object extraData)
        {
            System.IO.FileStream outFile = (System.IO.FileStream)extraData;

            outFile.Write(buf, 0, size * nmemb);

            return size * nmemb;
        }

        public static Int32 OnReadData(Byte[] buf, Int32 size, Int32 nmemb,
            Object extraData)
        {
            System.IO.FileStream inFile = (System.IO.FileStream)extraData;

            int totalBytesRead = 0;

            int bytesRead = 0;
            while (totalBytesRead <= size * nmemb)
            {
                bytesRead = inFile.Read(buf, totalBytesRead, (size * nmemb) - totalBytesRead);
                if (bytesRead <= 0) break;

                totalBytesRead += bytesRead;
            }

            return totalBytesRead;
        }
    }
}
